package org.jpropelleralt.jview.ref.impl;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;

import org.jpropelleralt.box.Box;
import org.jpropelleralt.box.Boxes;
import org.jpropelleralt.change.Change;
import org.jpropelleralt.jview.JView;
import org.jpropelleralt.jview.swing.impl.ColorPaletteChooser;
import org.jpropelleralt.jview.swing.impl.OKCancelDialog;
import org.jpropelleralt.ref.Ref;
import org.jpropelleralt.view.View;
import org.jpropelleralt.view.Views;
import org.jpropelleralt.view.update.UpdateManager;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;

/**
 * An editing {@link View} for a {@link Ref} containing
 * a {@link Color}
 */
public class ColorView implements JView {

	private final Ref<Color> value;
	private final Ref<Boolean> locked;

	private JButton editButton;
	private JPanel swatch;
	private JPanel panel;
	
	private ColorPaletteChooser colorChooser;
	private final UpdateManager updateManager;
	
	private ColorView(Ref<Color> value, Ref<Boolean> locked, UpdateManager updateManager) {
		super();
		this.value = value;
		this.locked = locked;
		buildComponent();
		
		this.updateManager = updateManager;
		updateManager.registerUpdatable(this);

		value.features().addView(this);
		if (locked != null) {
			locked.features().addView(this);
		}
		
		//Initial update
		update();
	}

	/**
	 * Create a new {@link ColorView}
	 * @param value				The value to display, as a {@link Ref} of {@link Color}
	 * @param locked			While this is true, view will not allow editing
	 * @param updateManager		The {@link UpdateManager} to use
	 * @return					A new {@link ColorView}
	 */
	public static ColorView create(Ref<Color> value, Ref<Boolean> locked, UpdateManager updateManager) {
		return new ColorView(value, locked, updateManager);
	}
	
	/**
	 * Create a new {@link ColorView}
	 * @param value				The value to display, as a {@link Ref} of {@link Color}
	 * @param locked			While this is true, view will not allow editing
	 * @return					A new {@link ColorView}
	 */
	public static ColorView create(Ref<Color> value, Ref<Boolean> locked) {
		return new ColorView(value, locked, Views.updateManager());
	}
	
	/**
	 * Create a new {@link ColorView}
	 * @param value				The value to display, as a {@link Ref} of {@link Color}
	 * @return					A new {@link ColorView}
	 */
	public static ColorView create(Ref<Color> value) {
		return new ColorView(value, null, Views.updateManager());
	}
	
	@Override
	public void changes(Map<Box, Change> changes) {
		updateManager.updateRequiredBy(this);
	}
	
	/**
	 * Get the {@link JComponent} used for display/editing
	 * @return	The editing {@link JComponent}
	 */
	public JComponent getComponent() {
		return panel;
	}
	
	private void buildComponent() {
		swatch = new JPanel();
		swatch.setBackground(null);
		swatch.setOpaque(true);
		swatch.setBorder(new MatteBorder(1,1,1,1,Color.BLACK));

		JPanel surround = new JPanel();
		surround.setLayout(new BorderLayout());
		surround.add(swatch);
		surround.setBorder(new EmptyBorder(3,2,3,0));
		
		surround.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				edit();
			}
		});
		
		editButton = new JButton("Edit...");
		editButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				edit();
			}
		});
		
		FormLayout layout = new FormLayout("fill:16dlu:grow, $lcgap, pref", "fill:pref");
		
		DefaultFormBuilder builder = new DefaultFormBuilder(layout);
		
		// Fill the grid with components
		builder.append(surround);
		builder.append(editButton);
		panel = builder.getPanel();
		
		colorChooser = ColorPaletteChooser.create();
	}
	
	private void edit() {

		Color color = value.get();

		//Use a new dialog each time we edit
		OKCancelDialog dialog = new OKCancelDialog(colorChooser.getPanel(), null, "Select color", "OK", "Cancel");
		colorChooser.setPreviousColor(color);
		dialog.setVisible(true);

		//This runs when the dialog is closed
		if (dialog.lastClickWasOK()) {
			Color newColor = colorChooser.getSelectedColor();
			if (color != null) {
				value.set(newColor);
			}
		}

		dialog.dispose();
	}
	
	@Override
	public void update() {
		Color color = value.get();
		boolean nowNull = (color == null);
		
		if (nowNull) {
			color = Color.BLACK;
		}
		
		if (!swatch.getBackground().equals(color)) {
			swatch.setBackground(color);
		}
		
		boolean enabled = !nowNull && !Boxes.isTrue(locked);

		if (editButton.isEnabled() != enabled) {
			editButton.setEnabled(enabled);
		}
	}

	@Override
	public boolean selfNaming() {
		//Just displays value
		return false;
	}
	
	@Override
	public Format format() {
		return Format.SINGLE_LINE;
	}
}
